home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgramD2.iso / Borland / Borland C++ V5.02 / XREF.PAK / XREF.CPP < prev    next >
C/C++ Source or Header  |  1997-05-06  |  15KB  |  409 lines

  1. /*------------------------------------------------------------------------*/
  2. /*                                                                        */
  3. /*  XREF.CPP                                                              */
  4. /*                                                                        */
  5. /*  Copyright (c) 1993, 1995 Borland International                        */
  6. /*  All Rights Reserved.                                                  */
  7. /*                                                                        */
  8. /*  Text cross referencing example                                        */
  9. /*                                                                        */
  10. /*------------------------------------------------------------------------*/
  11.  
  12. /*------------------------------------------------------------------------*/
  13. /*                                                                        */
  14. /*  Containers Used: TBinarySearchTreeImp, TIBinarySearchTreeImp,         */
  15. /*  TSVectorImp.                                                          */
  16. /*  Other Classes: string.                                                */
  17. /*                                                                        */
  18. /*  Data files provided: trivial.dat and text.dat.                        */
  19. /*------------------------------------------------------------------------*/
  20.  
  21. #include <classlib/binimp.h>
  22. #include <classlib/vectimp.h>
  23. #include <services/cstring.h>
  24. #include <services/regexp.h>
  25. #include <fstream.h>
  26. #include <strstrea.h>
  27. #include <iomanip.h>
  28. #include <dir.h>
  29.  
  30. // MaxRefs is the maximum number of references that a word can have 
  31. // before it is rejected (not put into the cross reference table).
  32. //
  33. const unsigned MaxRefs = 6;
  34.  
  35. // Indicates the minimum length a word must have to be a candidate to be
  36. // put into the cross reference table.
  37. //
  38. const unsigned MinWordLength = 3;
  39.  
  40. /*------------------------------------------------------------------------*/
  41. /*                                                                        */
  42. /*  class TRecord                                                         */
  43. /*                                                                        */
  44. /*      Keeps track of the lines that a word appears on.                  */
  45. /*                                                                        */
  46. /*  Public Interface:                                                     */
  47. /*                                                                        */
  48. /*      TRecord( string word, unsigned lineNum );                         */
  49. /*                                                                        */
  50. /*          Creates record with given word and line number.               */
  51. /*                                                                        */
  52. /*      int operator == ( const TRecord& record ) const;                  */
  53. /*                                                                        */
  54. /*          Returns 1 if the Words compare equal, 0 otherwise.            */
  55. /*                                                                        */
  56. /*      int operator < ( const TRecord& record ) const;                   */
  57. /*                                                                        */
  58. /*          Returns 1 if the Word in this record is lexically before the  */
  59. /*          Word in the record parameter.                                 */
  60. /*                                                                        */
  61. /*      int AddLineRef( unsigned lineNum );                               */
  62. /*                                                                        */
  63. /*          Add given page number to word's list of line references.  If  */
  64. /*          the number of references >= 'MaxRefs' then return 0 and       */
  65. /*          do not enter given line number in line ref list, else         */
  66. /*          return 1                                                      */
  67. /*                                                                        */
  68. /*      string GetWord() const;                                           */
  69. /*                                                                        */
  70. /*          Return copy of 'Word' member.                                 */
  71. /*                                                                        */
  72. /*  Global functions:                                                     */
  73. /*                                                                        */
  74. /*      ostream& operator << ( ostream&, const TRecord& );                */
  75. /*                                                                        */
  76. /*          Writes the record to the ostream.                             */
  77. /*                                                                        */
  78. /*------------------------------------------------------------------------*/
  79.  
  80. class TRecord
  81. {
  82.  
  83. public:
  84.  
  85.     TRecord( const string& word, unsigned lineNum );
  86.  
  87.     int operator == ( const TRecord& record ) const;
  88.     int operator < ( const TRecord& record ) const;
  89.     int AddLineRef( unsigned lineNum );
  90.     string GetWord() const;
  91.     unsigned GetPageCount() const;
  92.     friend ostream& operator << ( ostream&, const TRecord& );
  93.  
  94. private:
  95.  
  96.     string Word;
  97.     TSVectorImp<unsigned> LineRef;
  98.  
  99. };
  100.  
  101. TRecord::TRecord( const string& word, unsigned lineNum ) :
  102.     Word( word ),
  103.     LineRef(MaxRefs)
  104. {
  105.     AddLineRef(lineNum);
  106. }
  107.  
  108. inline int TRecord::operator == ( const TRecord& record ) const
  109. {
  110.     return Word == record.Word;
  111. }
  112.  
  113. inline int TRecord::operator < ( const TRecord& record ) const
  114. {
  115.     return Word < record.Word;
  116. }
  117.  
  118. int TRecord::AddLineRef( unsigned lineNum )
  119. {
  120.     if( LineRef.Count() < MaxRefs )
  121.         {
  122.         if( LineRef.Find( lineNum ) == UINT_MAX )
  123.             LineRef.Add( lineNum );
  124.         return 1;
  125.         }
  126.     return 0;
  127. }
  128.  
  129. string TRecord::GetWord() const
  130. {
  131.     return Word;
  132. }
  133.  
  134. unsigned TRecord::GetPageCount() const
  135. {
  136.     return LineRef.Count();
  137. }
  138.  
  139. ostream& operator << ( ostream& os, const TRecord& rec )
  140. {
  141.     char buf[80];
  142.     ostrstream temp( buf, sizeof(buf) );
  143.     temp.setf( ios::left );
  144.  
  145.     temp << setw(20) << rec.Word;
  146.  
  147.     unsigned i;
  148.     for(i = 0; i < rec.LineRef.Count()-1; i++ )
  149.         {
  150.         temp << rec.LineRef[i] << ',';  // write all but last line number
  151.         }                               // followed by a comma
  152.     temp << rec.LineRef[i] << ends;     // write last line number
  153.  
  154.     os << buf;
  155.     return os;
  156. }
  157.  
  158. /*------------------------------------------------------------------------*/
  159. /*                                                                        */
  160. /*  class TCrossRef                                                       */
  161. /*                                                                        */
  162. /*      Responsible for reading a trivial word file and text file,        */
  163. /*      then creating and printing a cross reference list of the words    */
  164. /*      in the text file.  The trivial word file contains words that will */
  165. /*      not be included in the cross reference.  The text file consists   */
  166. /*      of ordinary ASCII text. The ouput is an alphabetical list of      */
  167. /*      words in the text file and the numbers of the lines on which they */
  168. /*      occur.                                                            */
  169. /*                                                                        */
  170. /*  Public Interface:                                                     */
  171. /*                                                                        */
  172. /*      TCrossRef( string trivialWordFileName, string textFileName );     */
  173. /*                                                                        */
  174. /*          Read trivial word file and store in a binary search tree.     */
  175. /*          Then read each word in from the text file and process it.     */
  176. /*          If either file cannot be opened or an error occurs during     */
  177. /*          reading a TXFileReadError exception is thrown.                */
  178. /*                                                                        */
  179. /*      ~TCrossRef();                                                     */
  180. /*                                                                        */
  181. /*          Flushes the list of words in the cross reference.             */
  182. /*                                                                        */
  183. /*      PrintCrossReferences( ostream& );                                 */
  184. /*                                                                        */
  185. /*          Print each word and the lines it appears on in the text file. */
  186. /*                                                                        */
  187. /*      Nested class TXFileReadError.                                     */
  188. /*                                                                        */
  189. /*          An object of type TXFileReadError is thrown if either the     */
  190. /*          trivial word or cross reference file could not be opened or   */
  191. /*          read. To find out which file it was use the WhichFile()       */
  192. /*          member function.                                              */
  193. /*                                                                        */
  194. /*------------------------------------------------------------------------*/
  195.  
  196. class TCrossRef
  197. {
  198.  
  199. public:
  200.  
  201.     class TXFileReadError : public xmsg
  202.     {
  203.     public:
  204.         TXFileReadError( const string& file )
  205.             : xmsg( string( "Error reading file: " ) + file ), File(file) {}
  206.  
  207.         string WhichFile() const;
  208.  
  209.     private:
  210.         string File;
  211.     };
  212.  
  213.     TCrossRef( string trivialWordFileName, string textFileName );
  214.     ~TCrossRef();
  215.  
  216.     void PrintCrossReferences( ostream& );
  217.  
  218. private:
  219.  
  220.     void ReadTrivialWords( string trivialWordFileName );
  221.     void ReadWords( string bookWordFileName );
  222.     void ProcessWord( string word, unsigned page );
  223.  
  224.     static void PrintRecord( TRecord _BIDSFAR &o, void _BIDSFAR*arg );
  225.     void Reject( string word, string reason );
  226.  
  227.     TBinarySearchTreeImp<string> RejectedWords;
  228.     TIBinarySearchTreeImp<TRecord> IndexOfWords;
  229.     unsigned CurrentPage;
  230.  
  231. };
  232.  
  233. //
  234. //  Construct a TCrossRef object, reading words from the file
  235. //  specified by trivialWordFileName into the tree of rejected
  236. //  words, then reading the text from the file specified by
  237. //  testFileName and building the cross reference tree.
  238. //
  239. TCrossRef::TCrossRef( string trivialWordFileName, string textFileName ) :
  240.     CurrentPage(1)
  241. {
  242.     ReadTrivialWords( trivialWordFileName );
  243.     ReadWords( textFileName );
  244. }
  245.  
  246. //
  247. //  Clean up.
  248. //
  249. TCrossRef::~TCrossRef()
  250. {
  251.     IndexOfWords.Flush(1);
  252. }
  253.  
  254. //
  255. //  Read trivial words from the specified file, convert them
  256. //  to lower case, and store them in the rejected words tree.
  257. //
  258. void TCrossRef::ReadTrivialWords( string trivialWordFileName )
  259. {
  260.     ifstream TrivialWordFile( trivialWordFileName.c_str() );
  261.     if( !TrivialWordFile )
  262.         throw TXFileReadError( trivialWordFileName );
  263.  
  264.     string wordFromFile;
  265.  
  266.     while( TrivialWordFile >> wordFromFile )
  267.         {
  268.         wordFromFile.to_lower();
  269.         RejectedWords.Add( wordFromFile );
  270.         }
  271. }
  272.  
  273. //
  274. //  Read words from the specified file and insert them into
  275. //  the cross reference tree. Along the way, convert to lower
  276. //  case and strip out any non-text characters and all text 
  277. //  following any such characters within a word. (Quick and
  278. //  dirty hack for dealing with possessives, etc.)
  279. //
  280. void TCrossRef::ReadWords( string textFileName )
  281. {
  282.     ifstream XrefFile( textFileName.c_str() );
  283.  
  284.     if( !XrefFile )
  285.         throw TXFileReadError( textFileName );
  286.  
  287.     TRegexp WordOnly( "[a-z]+" );
  288.     unsigned currentLine = 1;
  289.     while( XrefFile )
  290.         {
  291.         char buf[128];
  292.         XrefFile.getline( buf, sizeof(buf) );
  293.         if( XrefFile )
  294.             {
  295.             string word;
  296.             istrstream in( buf );
  297.             while( in )
  298.                 {
  299.                 in >> word;
  300.                 if( in )
  301.                     {
  302.                     word.to_lower();    // store as lower case
  303.  
  304.                     // use WordOnly as filter to strip out trailing
  305.                     // non-text characters and any characters following.
  306.                     ProcessWord( word(WordOnly), currentLine );
  307.                     }
  308.                 }
  309.             currentLine++;
  310.             }
  311.         }
  312. }
  313.  
  314. //
  315. //  Figure out whether a word should be added to the cross
  316. //  reference tree. If not, explain. If so, add it.
  317. //
  318. void TCrossRef::ProcessWord( string word, unsigned lineNum )
  319. {
  320.  
  321.     if( RejectedWords.Find( word ) != 0 )
  322.         Reject( word, "found in rejected word file" );
  323.     else if( word.length() < MinWordLength )
  324.         Reject( word, "too short" );
  325.     else
  326.         {
  327.         TRecord *recordToUpdate = IndexOfWords.Find( &TRecord(word,lineNum) );
  328.         if( recordToUpdate == 0 )
  329.             {
  330.             //  The word isn't in the tree yet. Add a
  331.             //  new record to contain it.
  332.             IndexOfWords.Add( new TRecord(word,lineNum) );
  333.             }
  334.         else if( recordToUpdate->GetPageCount() >= MaxRefs )
  335.             {
  336.             Reject( word, "reference count too large" );
  337.             IndexOfWords.Detach( recordToUpdate, 1 );
  338.             }
  339.         else
  340.             {
  341.             //  The word is already in the tree. Add the
  342.             //  additional line reference.
  343.             recordToUpdate->AddLineRef( lineNum );
  344.             }
  345.         }
  346. }
  347.  
  348. //
  349. //  Callback for ForEach() to print a record to the stream
  350. //  pointed to by 'arg'.
  351. //
  352. void TCrossRef::PrintRecord( TRecord &o, void *arg )
  353. {
  354.     (*(ostream*)arg) << o << endl;
  355. }
  356.  
  357. //
  358. //  Print the cross references to the specified stream.
  359. //
  360. void TCrossRef::PrintCrossReferences( ostream& os )
  361. {
  362.     os << "References:\n\n";
  363.     os.setf( ios::left, ios::adjustfield );
  364.     IndexOfWords.ForEach( PrintRecord, &os );
  365. }
  366.  
  367. //
  368. //  Process rejected words by adding the word to the
  369. //  rejected words list and printing a message indicating
  370. //  why the word was rejected.
  371. //
  372. void TCrossRef::Reject( string word, string reason )
  373. {
  374.     RejectedWords.Add( word );
  375.     cerr << '\'' << word << "' rejected, " << reason << endl;
  376. }
  377.  
  378. string TCrossRef::TXFileReadError::WhichFile() const
  379. {
  380.     return File;
  381. }
  382.  
  383. /*------------------------------------------------------------------------*/
  384.  
  385. int main()
  386. {
  387.     try
  388.         {
  389.         string TrivialWordFile;
  390.         string XrefFile;
  391.  
  392.         cerr << "Enter name of trivial word file: ";
  393.         cin >> TrivialWordFile;
  394.  
  395.         cerr << "Enter name of file to cross reference: ";
  396.         cin >> XrefFile;
  397.  
  398.         TCrossRef CrossRef( TrivialWordFile, XrefFile );
  399.         CrossRef.PrintCrossReferences( cout );
  400.  
  401.         }
  402.     catch( const TCrossRef::TXFileReadError& xReadError )
  403.         {
  404.         cout << xReadError.why() << endl;
  405.         return 1;
  406.         }
  407.     return 0;
  408. }
  409.